/**
 *	@file	string_util.c
 *	@brief	safe string functions.
 *
 *	@author	Forrest.zhang
 */

#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#include "stdext.h"

int 
str_array_free(char *array[])
{
	char **p;

	if (unlikely(!array))
		return -1;

	p = array;
	while (*p != NULL) {
		free(*p);
		p++;
	}

	free(array);

	return 0;
}

int
str_len(const char *str, size_t len)
{
        register const char *p;
        register const char *end;

        if (unlikely(!str || len < 1))
                return -1;

	/* get @str end pos */
        p = str;
        end = str + len;
        while (*p && p < end)
                p++;

	/* @str is not zero-end */
	if (p == end)
		return -1;

	/* count does not include NUL */
        return (p - str);
}


int
str_copy(char *dst, size_t dlen, const char *src, size_t slen)
{
	register const char *p, *p1;
	register const char *end, *end1;
        register size_t n;

        if (unlikely(!dst || !src || dlen < 1 || slen < 1))
                return -1;

	/* get @dst end pos */
	p = dst;
	end = dst + dlen;
	while (*p && p < end)
		p++;

	/* @dst is not zero-end */
	if (p == end)
		return -1;

	/* get @src end pos */
	p1 = src;
	end1 = src + slen;
	while (*p1 && p1 < end1)
		p1++;
	
	/* calc @src real length */
	slen = p1 - src;

        /* Copy as many bytes as will fit */
        n = (dlen - 1) < slen ? (dlen - 1) : slen;
        memcpy(dst, src, n);

        /* Not enough room in dst, add NUL and traverse rest of src */
        dst[n]='\0';

        /* count does not include NUL */
        return n;
}


int 
str_cat(char *dst, size_t dlen, const char *src, size_t slen)
{
	register char *p;
        register const char *p1;
        register const char *end, *end1;
        register size_t n;

	/* get @dst end pos */
	p = dst;
	end = dst + dlen;
	while (*p && p < end)
		p++;
	
	/* @dst is not zero-end */
	if (p == end)
		return -1;

	/* calc @dst spare space */
	n = end - p - 1;
	if (n <= 0)
		return 0;

	/* get @src end pos */
	p1 = src;
	end1 = src + slen;
	while (*p1 && p1 < end1)
		p1++;

	/* calc @src real length */
	slen = p1 - src;

	/* copy string */
	n = n > slen ? slen : n;
	memcpy(p, src, n);

	/* zero-end @dst */
	p[n] = 0;
       
        return n;
}

int 
str_cmp(const char *str1, size_t len1, const char *str2, size_t len2)
{
	register const char *p1, *p2;
	register const char *end1, *end2;
	register size_t n;
	register int ret;

	if (str1 == str2 && len1 == len2)
		return 0;

	if (unlikely(!str1 || !str2 || len1 < 1 || len2 < 1))
		return -1;

	/* get @str end pos */
	p1 = str1;
	end1 = str1 + len1;
	while (*p1 && p1 < end1)
		p1++;

	/* calc @str1 real length */
	len1 = p1 - str1;

	/* get @str2 end pos */
	p2 = str2;
	end2 = str2 + len2;
	while (*p2 && p2 < end2)
		p2++;

	/* calc @str2 real length */
	len2 = p2 - str2;

	n = len1 > len2 ? len2 : len1;
	ret = memcmp(str1, str2, n);

	return ret == 0 ? (len1 - len2) : ret;
}

char *
str_chr(const char *str, size_t len, char c)
{
        register const char *p;
	register const char *end;

        if (unlikely(!str || len < 1))
                return NULL;	

	/* get @str end pos */
        p = str;
	end = str + len;
	while (*p && p < end)
		p++;

	/* @str is not zero-end */
	if (p == end)
		return NULL;

	/* search char @c */
	end = p;
	p = str;
	while (*p && p < end) {
		/* found char @c and return it pos */
                if (*p == c)
                        return (char *)p;
                p++;
        }

        return NULL;
}


char *
str_rchr(const char *str, size_t len, char c)
{
        register const char *p;
        register const char *end;
 
        if (unlikely(!str || len < 1))
                return NULL;

	/* get @str end pos */
        p = str;
        end = str + len;
	while (*p && p < end)
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return NULL;

	do {
		if (*p == c)
			return (char *)p;
		p--;
	} while (p >= str);

	return NULL;
}


char *
str_str(const char *str, size_t len, const char *substr, size_t slen)
{
        register const char *p;
        register const char *end;

	/* invalid parameter */
        if (unlikely(!str || !substr || len < 1 || slen < 1))
                return NULL;

	/* get @str end pos */
        p = str;
        end = str + len;
	while (*p && p < end)
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return NULL;
	
	/* calc @str real length */
	len = p - str;

	/* get @substr end pos */
	p = substr;
	end = substr + slen;
	while (*p && p < end) 
		p++;
	
	/* calc @substr real length */
	slen = p - substr;

	/* @substr large than @str */
	if (slen > len)
		return NULL;

	/* search @substr */
	p = str;
	end = str + len - slen;
        do {
                if (memcmp(p, substr, slen) == 0)
                        return (char *)p;
                p++;
        } while (p <= end);

	/* not found */
        return NULL;
}


char *
str_rstr(const char *str, size_t len, const char *substr, size_t slen)
{
        register const char *p;
	register const char *end;

        if (unlikely(!str || !substr || len < 1 || slen < 1))
                return NULL;

	/* get @str end pos */
        p = str;
        end = str + len;
	while (*p && p < end) 
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return NULL;
	
	/* calc @str real length */
	len = p - str;

	/* get @substr end pos */
	p = substr;
	end = substr + slen;
	while (*p && p < end)
		p++;
	
	/* calc @substr real length */
	slen = p - substr;

	/* @substr large than @str */
	if (slen > len)
		return NULL;

	/* reverse search @substr */
	p = str + len - slen;
        do {
                if (memcmp(p, substr, slen) == 0)
                        return (char *)p;
                p--;
        } while (p >= str);

	/* not found */
        return NULL;
}


char *
str_casestr(const char *str, size_t len, const char *substr, size_t slen)
{
	        register const char *p;
        register const char *end;

	/* invalid parameter */
        if (unlikely(!str || !substr || len < 1 || slen < 1))
                return NULL;

	/* get @str end pos */
        p = str;
        end = str + len;
	while (*p && p < end)
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return NULL;
	
	/* calc @str real length */
	len = p - str;

	/* get @substr end pos */
	p = substr;
	end = substr + slen;
	while (*p && p < end) 
		p++;
	
	/* calc @substr real length */
	slen = p - substr;

	/* @substr large than @str */
	if (slen > len)
		return NULL;

	/* search @substr */
	p = str;
	end = str + len - slen;
        do {
                if (strncasecmp(p, substr, slen) == 0)
                        return (char *)p;
                p++;
        } while (p <= end);

	/* not found */
        return NULL;
}


char *
str_caserstr(const char *str, size_t len, const char *substr, size_t slen)
{
        register const char *p;
	register const char *end;

        if (unlikely(!str || !substr || len < 1 || slen < 1))
                return NULL;

	/* get @str end pos */
        p = str;
        end = str + len;
	while (*p && p < end) 
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return NULL;
	
	/* calc @str real length */
	len = p - str;

	/* get @substr end pos */
	p = substr;
	end = substr + slen;
	while (*p && p < end)
		p++;
	
	/* calc @substr real length */
	slen = p - substr;

	/* @substr large than @str */
	if (slen > len)
		return NULL;

	/* reverse search @substr */
	p = str + len - slen;
        do {
                if (strncasecmp(p, substr, slen) == 0)
                        return (char *)p;
                p--;
        } while (p >= str);

	/* not found */
        return NULL;
}


char *
str_token(char *str, size_t len, const char *delim, size_t dlen, char **off)
{
	register char *p, *ptr;
	register char *end;
	register const char *p1;
	register const char *end1;

	if (unlikely(!delim || !off || len < 1 || dlen < 1))
		return NULL;

	/* get @str start pos */
	if (str) {
		/* get @str end pos */
		p = str;		
		end = str + len;
		while (*p && p < end)
			p++;
		
		/* @str is not zero-end */
		if (p == end)
			return NULL;

		ptr = str;
	}
	else {
		/* @*off is NULL, no next token */
		if (*off == NULL)
			return NULL;

		p = *off;
		ptr = *off;
	}

	end1 = delim + dlen;
	while (*p) {
		p1 = delim;
		while (*p1 && p1 < end1) {
			if (*p == *p1) {
				*p = 0;
				*off = p + 1;
				return ptr;
			}
			p1++;
		}
		p++;
	}

	/* not match delim, next call will return NULL */
	*off = NULL;
        return str;
}


int
str_is_blank(const char *str, size_t len)
{
        register const char *p = NULL;
        register const char *end;

        if (unlikely(!str || len < 1))
                return 1;

	/* get @str end pos */
	p = str;
        end = str + len;
	while (*p && p < end) 
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return 0;

	end = p;
	p = str;
        while (isspace(*p) && p < end)
                p++;

	/* all chars are space char */
        if (p == end)
                return 1;

        return 0;
}


char *
str_strip_begin(const char* str, size_t len)
{
        register const char *p;
        register const char *end;

        if (unlikely(!str || len < 1))
                return NULL;

	/* get @str end pos */
	p = str;
        end = str + len;
	while (*p && p < end) 
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return NULL;
	
	end = p;
	p = str;
	do {
		if (!isspace(*p))
			return (char *)p;
		p++;
	} while(p < end);

        return NULL;
}


char *
str_strip_end(const char *str, size_t len)
{
        register const char *p;
        register const char *end;

	if (unlikely(!str || len < 1))
		return NULL;

	/* get @str end pos */
	p = str;
        end = str + len;
	while (*p && p < end) 
		p++;
	
	/* @str is not zero-end */
	if (p == end)
		return NULL;

	do {
		if (!isspace(*p))
			return (char *)p;
		p--;
	} while (p >= str);

	return NULL;
}

                                                                           

int
mem_copy(void *dst, size_t dlen, const void *src, size_t slen)
{
        register size_t n = dlen < slen ? dlen : slen;

        if (unlikely(!dst || !src))
                return 0;

        memcpy(dst, src, n);

        return n;
}

int
mem_move(void *dst, size_t dlen, const void *src, size_t slen)
{
        register size_t n;

        if (unlikely(!dst || !src || dlen < 1 || slen < 1))
                return -1;

        n = dlen < slen ? dlen : slen;
        memmove(dst, src, n);

        return n;
}


void *
mem_chr(const void *str, size_t len, unsigned char c)
{
        register const char *p;
	register const char *end;

        if (unlikely(!str || len < 1))
                return NULL;

        p = str;
	end = str + len;
	while (p < end) {
		if (*p == c)
			return (char *)p;
		p++;
	}

        return NULL;
}


void *
mem_rchr(const void *str, size_t len, unsigned char c)
{
        register const char *begin;
        register const char *p;

        if (unlikely(!str || len < 1))
                return NULL;

        begin = str;
        p = str + len;
        while (p >= begin) {
                if (*p == c)
                        return (void *)p;
                p--;
        }

        return NULL;
}

